`ifndef APB_SLV_DRIVER_SV
`define APB_SLV_DRIVER_SV


class apb_slv_driver extends uvm_driver #(apb_transaction);

	virtual apb_interface vif;
	apb_transaction 			tr;
	apb_mem #(32, 32)			mem;

	`uvm_component_utils(apb_slv_driver)

	function new(string name = "apb_slv_driver", uvm_component parent = null);
		super.new(name, parent);
	endfunction : new

	function void build_phase(uvm_phase phase);
		super.build_phase(phase);

		mem = apb_mem #(32, 32)::type_id::create("mem");
		tr 	= apb_transaction::type_id::create("tr");
		if(!uvm_config_db #(virtual apb_interface)::get(this, "", "vif", vif))
			`uvm_fatal("GETCFG", "cannot get vif from config DB!")
	endfunction : build_phase

	function void connect_phase(uvm_phase phase);
		super.connect_phase(phase);
	endfunction : connect_phase

	task main_phase(uvm_phase phase);
		vif.cb_slv.PREADY 	<= 1'b1;
		vif.cb_slv.PSLVERR 	<= 1'b0;
		while(1) begin
			@(vif.cb_slv);
			if(!vif.cb_slv.PENABLE & vif.cb_slv.PSEL) begin
				seq_item_port.get_next_item(tr);
				drive_one_pkt(tr);
				seq_item_port.item_done();
			end
		end
	endtask : main_phase

	task drive_one_pkt(apb_transaction tr);
		int nready_num;

		nready_num = tr.nready_num;
		while(nready_num) begin
			vif.cb_slv.PREADY <= 1'b0;
			nready_num --;
			@(vif.cb_slv);
		end
		
		vif.cb_slv.PREADY <= 1'b1;
		vif.cb_slv.PSLVERR <= tr.pslverr;

		// write
		if(vif.cb_slv.PWRITE) begin
			if(!tr.pslverr)
				mem.put_data(vif.cb_slv.PADDR, vif.cb_slv.PWDATA);
		end
		// read
		else begin
			if(!tr.pslverr)
				vif.cb_slv.PRDATA <= mem.get_data(vif.cb_slv.PADDR);
			else
				vif.cb_slv.PRDATA <= tr.rdata; // drive a random num, not a num in memory
		end
	endtask : drive_one_pkt

endclass : apb_slv_driver


`endif // APB_SLV_DRIVER_SV
